package com.yonyou.pmclouds.plan.controller;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import com.alibaba.dubbo.config.annotation.Reference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.basecom.consts.BillTypeConst;
import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.basecom.exception.IllegalRequestArgException;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;
import com.yonyou.pmclouds.basecom.util.CommonFieldConst;
import com.yonyou.pmclouds.basecom.util.DateUtils;
import com.yonyou.pmclouds.billtype.entity.BillTypeRefVO;
import com.yonyou.pmclouds.businessobject.util.SortListUtil;
import com.yonyou.pmclouds.milestone.entity.MilestoneVO;
import com.yonyou.pmclouds.milestone.service.IMilestoneQuery;
import com.yonyou.pmclouds.outter.nc.NcBillTypeCustomerQuery;
import com.yonyou.pmclouds.outter.nc.NcDeptCustomerQuery;
import com.yonyou.pmclouds.outter.user.UserInfoQuery;
import com.yonyou.pmclouds.outter.user.UserOperator;
import com.yonyou.pmclouds.plan.entity.*;
import com.yonyou.pmclouds.plan.plandriven.entity.NcDepartmentVO;
import com.yonyou.pmclouds.plan.plandriven.entity.NcDeptQueryParamVO;
import com.yonyou.pmclouds.plan.plandriven.rmiitf.IPlanDepartmemtQuery;
import com.yonyou.pmclouds.plan.rmiitf.ProjectPlanMaintain;
import com.yonyou.pmclouds.plan.rmiitf.ProjectPlanQuery;
import com.yonyou.pmclouds.project.entity.ProjectVO;
import com.yonyou.pmclouds.project.rmiitf.ProjectSimpleQuery;
import com.yonyou.pmclouds.team.entity.TeamMemberVO;
import com.yonyou.pmclouds.team.rmiitf.ProjectRefWithTeamQuery;
import com.yonyou.pmclouds.team.rmiitf.TeamMemberQuery;
import com.yonyou.pmclouds.user.entity.UserInfo;
import com.yonyou.pmclouds.user.entity.UserSearchResultVO;
import com.yonyou.pmclouds.workflow.aspect.PMApprove;
import lombok.extern.slf4j.Slf4j;
import net.sf.mpxj.*;
import net.sf.mpxj.mpp.MPPReader;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
@RestController
@RequestMapping("/projectPlan")
@Deprecated
public class ProjectPlanWebController {
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private ProjectPlanMaintain maintain;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private ProjectPlanQuery query;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private ProjectSimpleQuery projectSimpleQuery;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private TeamMemberQuery teamMemberQuery;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private ProjectRefWithTeamQuery refQuery;
    @Autowired
    private UserInfoQuery userInfoQuery;
    @Autowired
    private UserOperator userOperator;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private IMilestoneQuery milestoneQuery;
    @Reference(version = ApplicationConsts.APPLICATION_VERSION)
    private IPlanDepartmemtQuery departmemtQuery;
    @Autowired
    private NcBillTypeCustomerQuery ncBillTypeCustomerQuery;
    @Autowired
    private NcDeptCustomerQuery ncDeptCustomerQuery;

    private ObjectMapper objectMapper = new ObjectMapper();

    private ProjectPlanViewVO[] orginalViewVOs = null;

    private int totalFinishCount = 0;

    private int onTimeCount = 0;

    private static final String[] MANAGER_ID_FIELDS = {ProjectPlanViewVO.MANAGER};
    private static final String[] MANAGER_NAME_FIELDS = {ProjectPlanViewVO.MANAGER_NAME};
    private static final String[] USER_ID_FIELDS = {ProjectPlanViewVO.CREATOR};
    private static final String[] USER_NAME_FIELDS = {ProjectPlanViewVO.CREATOR_NAME};
    private static final String[] PROJECT_MANAGER_ID_FIELDS = {ProjectVO.MANAGER};
    private static final String[] PROJECT_MANAGER_NAME_FIELDS = {ProjectVO.MANAGER_NAME};

    // 进行中
    private static final String FILTER_CODE_ONGOING = "ongoing";
    // 已完成
    private static final String FILTER_CODE_COMPLETED = "completed";
    // 全部
    private static final String FILTER_CODE_ALL = "all";

    @GetMapping("/project")
    public ProjectPlanViewVO[] queryProject(@RequestParam("projectId") String projectId) throws BusinessException {
        ProjectPlanViewVO[] allVOs = query.queryLastViewByProject(projectId);
        if (ArrayUtils.isEmpty(allVOs)) {
            return allVOs;
        }
        setDependPlanName(allVOs);

        // 翻译单据类型、部门
        transAndFillViewVO(allVOs);

        String[] userFields = new String[]{ProjectPlanViewVO.MANAGER, ProjectPlanViewVO.CREATOR, ProjectPlanViewVO.PLAN_FINISHER};
        String[] userNameFields = new String[]{ProjectPlanViewVO.MANAGER_NAME, ProjectPlanViewVO.CREATOR_NAME,
                ProjectPlanViewVO.PLAN_FINISHER_NAME};
        userInfoQuery.tranUserInfoField(allVOs, userFields, userNameFields);
        Map<String, List<ProjectPlanViewVO>> childMap = genChildMap(allVOs);
        List<ProjectPlanViewVO> result = childMap.get(StringUtils.EMPTY);
        if (result == null) {
            return new ProjectPlanViewVO[0];
        } else {
            genCodeOfTree(null, result, childMap);
            ProjectPlanViewVO[] vos = result.toArray(new ProjectPlanViewVO[0]);
            log.error("查询结束："+new Date().getTime());
            return vos;
        }
    }

    /**
     * 翻译单据类型、部门
     *
     * @param allVOs
     */
    private void transAndFillViewVO(ProjectPlanViewVO[] allVOs) throws BusinessException {
        Set<String> billTypeCodeSet = new HashSet<>();
        Set<String> pkDeptSet = new HashSet<>();
        for (ProjectPlanViewVO plan : allVOs) {
            if (plan.getChildCount() == 0) {
                if (StringUtils.isNotEmpty(plan.getRelatedBillType()))
                    billTypeCodeSet.add(plan.getRelatedBillType());
            }

            if (StringUtils.isNotEmpty(plan.getPkDepartment()))
                pkDeptSet.add(plan.getPkDepartment());
        }

        // 查询单据类型名称
        Map<String, String> billTypeCodeToName = null;
        if (billTypeCodeSet.size() > 0) {
            billTypeCodeToName = queryBillTypeName(billTypeCodeSet.toArray(new String[0]));
        }

        // 查询部门名称
        Map<String, String> pkDeptToName = null;
        if (pkDeptSet.size() > 0) {
            pkDeptToName = queryDepartmentName(pkDeptSet.toArray(new String[0]));
        }

        for (ProjectPlanViewVO vo : allVOs) {
            if (billTypeCodeToName != null && StringUtils.isNotEmpty(vo.getRelatedBillType())) {
                vo.setRelatedBillTypeName(billTypeCodeToName.get(vo.getRelatedBillType()));
            }

            if (pkDeptToName != null && StringUtils.isNotEmpty(vo.getPkDepartment())) {
                vo.setDepartmentName(pkDeptToName.get(vo.getPkDepartment()));
            }
        }
    }

    /**
     * 查询单据类型名称
     *
     * @param billTypeCodes
     * @return
     */
    private Map<String, String> queryBillTypeName(String[] billTypeCodes) throws BusinessException {
        // 是否查询NC单据类型
        boolean isQueryNcBillType = false;
        for (String billType : billTypeCodes) {
            if (!BillTypeConst.WORKSTART_BILLTYPE.equals(billType) && !BillTypeConst.COMPREHENSIVE_ACCEPTANCE_BILLTYPE.equals(billType)) {
                isQueryNcBillType = true;
                break;
            }
        }

        List<BillTypeRefVO> billTypeRefVOs = new ArrayList<>();
        billTypeRefVOs.add(new BillTypeRefVO(BillTypeConst.WORKSTART_BILLTYPE, BillTypeConst.WORKSTART_BILLTYPE_NAME));
        billTypeRefVOs.add(new BillTypeRefVO(BillTypeConst.COMPREHENSIVE_ACCEPTANCE_BILLTYPE,
                BillTypeConst.COMPREHENSIVE_ACCEPTANCE_BILLTYPE_NAME));
        if (isQueryNcBillType) {
            // 查询nc单据类型
            BillTypeRefVO[] ncBillTypeRefVO = ncBillTypeCustomerQuery.queryRelatedBillTypeForPlan();
            if (ArrayUtils.isNotEmpty(ncBillTypeRefVO)) {
                billTypeRefVOs.addAll(Arrays.asList(ncBillTypeRefVO));
            }
        }

        Map<String, String> result = new HashMap<>();
        Map<String, String> codeToName = new HashMap<>();
        for (BillTypeRefVO refVO : billTypeRefVOs) {
            codeToName.put(refVO.getCode(), refVO.getName());
        }

        for (String code : billTypeCodes) {
            result.put(code, codeToName.get(code));
        }

        return result;
    }

    /**
     * 查询部门名称
     *
     * @param pkDepartments
     * @return
     */
    private Map<String, String> queryDepartmentName(String[] pkDepartments) throws BusinessException {
        Map<String, String> result = new HashMap<>();
        List<NcDepartmentVO> ncDepartmentVOs = ncDeptCustomerQuery
                .queryDepartmentFromNc(new NcDeptQueryParamVO(null, null, Arrays.asList(pkDepartments)));

        if (CollectionUtils.isEmpty(ncDepartmentVOs))
            return result;

        for (NcDepartmentVO vo : ncDepartmentVOs) {
            result.put(vo.getPkDept(), vo.getName());
        }

        return result;
    }

    /**
     * 项目计划一览表导出
     * @param response
     * @param projectId
     * @throws BusinessException
     * @throws IOException
     */
    @GetMapping("/export")
    public void queryProject2Export(HttpServletResponse response,@RequestParam("projectId") String projectId) throws BusinessException, IOException {
        //获取根项目树
        List<ProjectPlanViewVO> resultSet = mainBusiness(projectId);
        if (resultSet == null || resultSet.size() == 0) return;
        //全局里程碑数量容器(有序)
        Set<String> milestoneNumContainer = new LinkedHashSet();
        //子计划名称(sname)和子计划id(pkplan)的映射
        Map<String,String> pkPlanAndSname = new HashMap<>();
        //主数据容器(有序)
        Map<String,Map<String,List<Integer>>> mainContainer = new LinkedHashMap<>();
        //主数据处理
        mainDataProcess(resultSet,milestoneNumContainer,mainContainer,pkPlanAndSname);
        //序号，计划名称标题填充
        List<ExcelExportEntity> baseTitleContainer = baseTitleFill();
        //动态填充里程碑标题
        dynamicFillMilestonesTitle(milestoneNumContainer, baseTitleContainer);
        //构造要导出的Excel对应的容器
        List<Map<String, Object>> exportContainer = new ArrayList<>();
        //子项目容器
        List<String> subProjectContainer = new ArrayList<>(mainContainer.keySet());
        //动态填充数据体(以里程碑作为标题作为主导)
        dynamicFillMilestonesData(mainContainer, pkPlanAndSname,exportContainer, subProjectContainer);
        //执行导出
        exportProjectPlanTable(response, baseTitleContainer, exportContainer);
    }

    private List<ExcelExportEntity> baseTitleFill() {
        List<ExcelExportEntity> colList = new ArrayList();
        ExcelExportEntity numEntity = new ExcelExportEntity("序号", "numName");
        numEntity.setNeedMerge(true);
        colList.add(numEntity);

        ExcelExportEntity planNameEntity = new ExcelExportEntity("计划名称", "subPlanName");
        planNameEntity.setWidth(20);
        planNameEntity.setNeedMerge(true);
        colList.add(planNameEntity);
        return colList;
    }

    private void mainDataProcess(List<ProjectPlanViewVO> resultSet, Set<String> milestoneNumContainer,Map<String,Map<String,List<Integer>>> mainContainer,Map<String,String> pkPlanAndSname) {
        //每个项目的根节点(projectPlan),内层以逐个项目为单位遍历每个节点的里程碑数据
        for(ProjectPlanViewVO projectPlan : resultSet){
            Map<String,List<Integer>> processContainer = new LinkedHashMap<>();
            //整理节点里程碑数据到exportMap容器
            if(ArrayUtils.isNotEmpty(projectPlan.getChildren())){
                //具体处理
                queryChilds(projectPlan.getChildren(), processContainer);
            }else{
                //do nothing
            }
            milestoneNumContainer.addAll(processContainer.keySet());
            //以每个项目为单位绑定计算好的里程碑数据
            mainContainer.put(projectPlan.getPkPlan(),processContainer);
            pkPlanAndSname.put(projectPlan.getPkPlan(),projectPlan.getSname());
        }
    }

    private List<ProjectPlanViewVO> mainBusiness(String projectId) throws BusinessException {
        ProjectPlanViewVO[] allVOs = query.queryLastViewByProject(projectId);
        if (ArrayUtils.isEmpty(allVOs)) {
            return new ArrayList<>();
        }else{
            return genChildMap(allVOs).get(StringUtils.EMPTY);
        }
    }

    private void exportProjectPlanTable(HttpServletResponse response, List<ExcelExportEntity> baseTitleContainer, List<Map<String, Object>> exportContainer) throws IOException {
        response.setHeader("content-Type", "application/vnd.ms-excel");
        //下载文件的默认名称
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("项目计划一览表","UTF-8") + ".xls");
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("项目计划一览表", "项目计划一览表"), baseTitleContainer , exportContainer);
        response.setCharacterEncoding("UTF-8");
        workbook.write(response.getOutputStream());
    }

    private void dynamicFillMilestonesData(Map<String, Map<String, List<Integer>>> mainContainer, Map<String,String> pkPlanAndSname,List<Map<String, Object>> exportContainer, List<String> subProjectContainer) {
        for(int i = 0; i < subProjectContainer.size(); i++){
            int num = i;
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("numName", ++num);//序号
            dataMap.put("subPlanName", pkPlanAndSname.get(subProjectContainer.get(i)));//子计划名称

            Map<String,List<Integer>> map = mainContainer.get(subProjectContainer.get(i));
            //遍历里程碑名称和计算结果
            Set<String> milestoneNames = map.keySet();

            List<String> milestoneNamesLists = new ArrayList(milestoneNames);
            if(null != milestoneNamesLists && milestoneNamesLists.size() > 0){
                for(int j = 0; j < milestoneNamesLists.size(); j++){
                    String curentMilestoneName = milestoneNamesLists.get(j);
                    //根据索引平铺里程碑数据体(里程碑标题curentMilestoneName绑定总数量.已完成.完成百分比的集合milestoneDetailList)
                    List<Integer> tempCulResult = map.get(milestoneNamesLists.get(j));
                    Integer total = tempCulResult.get(0);//总数量
                    Integer finished = tempCulResult.get(1);//已完成数
                    String percent = calculatePercent(total, finished);//计算百分比
                    List<Map<String, Object>> milestoneDetailList = new ArrayList();
                    Map<String, Object> culMap = new HashMap();
                    culMap.put("total",total);
                    culMap.put("finished",finished);
                    culMap.put("percent",percent);
                    milestoneDetailList.add(culMap);

                    dataMap.put(curentMilestoneName,milestoneDetailList);
                }
            }
            exportContainer.add(dataMap);
        }
    }

    private String calculatePercent(Integer total, Integer finished) {
        double finishedD = finished;
        double totalD = total;
        double ratio = finishedD / totalD;
        // 创建一个数值格式化对象
        NumberFormat format = NumberFormat.getPercentInstance();
        //设置保留几位小数(目前没有设置)
        //format.setMaximumFractionDigits(2);
        return format.format(ratio);
    }

    private void dynamicFillMilestonesTitle(Set<String> milestoneNumContainer, List<ExcelExportEntity> baseTitleContainer) {
        List<String> milestones;
        if(milestoneNumContainer != null && milestoneNumContainer.size() > 0){
            milestones = new ArrayList(milestoneNumContainer);
            if(milestones != null && milestones.size() > 0){
                for(int j = 0; j < milestones.size(); j++){
                    String milestoneIndex = milestones.get(j);
                    String milestoneName = milestones.get(j);
                    ExcelExportEntity milestoneColGroup = new ExcelExportEntity(milestoneName, milestoneIndex);
                    List<ExcelExportEntity> milestoneColList = new ArrayList<ExcelExportEntity>();
                    milestoneColList.add(new ExcelExportEntity("总数量", "total"));
                    milestoneColList.add(new ExcelExportEntity("已完成量","finished"));
                    ExcelExportEntity percentEntity = new ExcelExportEntity("完成百分比", "percent");
                    percentEntity.setWidth(20);
                    milestoneColList.add(percentEntity);
                    milestoneColGroup.setList(milestoneColList);
                    baseTitleContainer.add(milestoneColGroup);
                }
            }
        }
    }

    private void queryChilds(ProjectPlanViewVO[] childs, Map<String,List<Integer>> processContainer) {
        //出口
        if(ArrayUtils.isEmpty(childs)){
            return;
        }
        for(ProjectPlanViewVO p : childs){
            //只查询里程碑名称有值得，null退出递归
            String milestoneName = p.getMilestoneName();
            //只考虑里程碑不为空的情况
            if(!StringUtils.isEmpty(milestoneName)){
                if(null != processContainer.get(milestoneName)){//查到里程碑的情况
                    //取出来
                    List<Integer> calculator = processContainer.get(milestoneName);
                    //(总数)计算+1
                    calculator.set(0, calculator.get(0).intValue() + 1);
                    //里程碑项目进度100%,(已完成)计算+1
                    if(p.getProgress().intValue() == 100){
                        calculator.set(1, calculator.get(1).intValue() + 1);
                    }else{//没完成
                        //do nothing
                    }
                    processContainer.put(milestoneName,calculator); //把计算后的数据存到map中
                }else{//有值，但map中没查到的情况,为每一个没有查到的subList初始化数据
                    if(p.getProgress().intValue() == 100){
                        processContainer.put(milestoneName, Arrays.asList(1,1));
                    }else{//没完成
                        processContainer.put(milestoneName,Arrays.asList(1,0));
                    }
                }
            }else{//为空
                //do nothing
            }
            //向当前节点的子节点查找,向下查找
            queryChilds(p.getChildren(),processContainer);
        }
    }

    /**
     * 统计分析计划看板接口
     * @param tenantId 租户ID
     * @param filterCode 页签过滤编码：ongoing（进行中）、completed（已完成）、all（全部）
     * @param sortField 排序字段，多个字段用逗号拼接
     * @param sortWay 排序方式，与排序字段对应，多个用逗号拼接
     * @return 项目VO
     */
    @GetMapping("/tenantId")
    public ProjectVO[] queryByTenantId(@RequestParam("tenantId") String tenantId, @RequestParam("filterCode") String filterCode,
                                       @RequestParam("sortField") String sortField,
                                       @RequestParam("sortWay") String sortWay, @RequestParam("orgId") String orgId) throws BusinessException {
        //查询租户下所有项目计划
        ProjectPlanViewVO[] planViewVOS = query.queryPlanByTenantId(tenantId);
        if (ArrayUtils.isEmpty(planViewVOS)) {
            return new ProjectVO[0];
        }
        // 将子计划放到父计划中
        Map<String, List<ProjectPlanViewVO>> childMap = genChildMap(planViewVOS);
        // 获取根节点计划
        List<ProjectPlanViewVO> result = childMap.get(StringUtils.EMPTY);
        if (result == null) {
            return new ProjectVO[0];
        } else {
            ProjectPlanViewVO[] vos = result.toArray(new ProjectPlanViewVO[0]);
            orginalViewVOs = vos;
            // 遍历计划查找叶子节点计划进度，从叶子节点推算父级节点进度
            findLeafNode(vos,orginalViewVOs);
            // 将计划按项目进行分类组装
            List<ProjectVO> projectVOList = this.coverToProjectVo(vos, filterCode, orgId);
            SortListUtil.sort(projectVOList, sortField, sortWay);
            ProjectVO[] allVOs = projectVOList.toArray(new ProjectVO[0]);
            userInfoQuery.tranUserInfoField(allVOs, PROJECT_MANAGER_ID_FIELDS, PROJECT_MANAGER_NAME_FIELDS);
            return allVOs;
        }
    }

    /**
     * 计划编制、计划模板，查询nc部门
     *
     * @param pkOrg 组织主键
     * @param pkProject 项目主键
     * @return
     * @throws BusinessException
     */
    @RequestMapping("/queryDept")
    public List<NcDepartmentVO> queryDepartment(@RequestParam(value = "pkOrg", required = false) String pkOrg,
                                                @RequestParam(value = "pkProject", required = false) String pkProject) throws BusinessException {
        if (StringUtils.isEmpty(pkOrg) && StringUtils.isEmpty(pkProject))
            return new ArrayList<>();

        return ncDeptCustomerQuery.queryDepartmentFromNc(new NcDeptQueryParamVO(pkOrg, pkProject, null));
    }

    /**
     * 通过查找叶子节点计划进度计算父级进度
     * @param planViewVOS 计划VO
     */
    public void findLeafNode(ProjectPlanViewVO[] planViewVOS,ProjectPlanViewVO[] orginalViewVOs) {
        if (ArrayUtils.isEmpty(planViewVOS))
            return;

        for (ProjectPlanViewVO planViewVO : planViewVOS) {
            if (planViewVO.getChildCount() != 0) {
                this.findLeafNode(planViewVO.getChildren(),orginalViewVOs);
            } else {
                if (!planViewVO.getPkPlan().equalsIgnoreCase(planViewVO.getPkRoot())) {
                    this.computedParentProgress(planViewVOS, planViewVO,orginalViewVOs);
                }
            }
        }
    }

    /**
     * 通过父节点下的所有叶子节点计算父节点进度
     * @param planViewVOS 父节点的所有子级节点
     * @param currentNode 当前节点
     */
    public void computedParentProgress(ProjectPlanViewVO[] planViewVOS, ProjectPlanViewVO currentNode,ProjectPlanViewVO[] orginalViewVOs) {
        int totalDay = 0;
        int totalPresent = 0;
        for (ProjectPlanViewVO planViewVO : planViewVOS) {
            totalDay += planViewVO.getExpeDuration();
            totalPresent += planViewVO.getProgress() * planViewVO.getExpeDuration();
        }

        int currentPresent = 0;
        if (totalDay > 0) {
            currentPresent = (int) Math.floor(totalPresent / totalDay);
        }

        // 将进度赋给父级节点
        this.setProgressToParent(currentPresent, currentNode, orginalViewVOs);
    }

    /**
     * 将进度赋给父节点
     * @param currentPresent 当前进度
     * @param currentNode 当前节点
     * @param planViewVOS 所有计划节点
     */
    public void setProgressToParent(int currentPresent, ProjectPlanViewVO currentNode, ProjectPlanViewVO[] planViewVOS) {
        if (ArrayUtils.isEmpty(planViewVOS))
            return;

        for (ProjectPlanViewVO planViewVO : planViewVOS) {
            if (planViewVO.getPkPlan().equalsIgnoreCase(currentNode.getPkParent())) {
                planViewVO.setProgress(currentPresent);
                if (planViewVO.getPkParent() != null) {
                    // 如果父级节点还有父级，则继续向上遍历计算进度
                    this.findParentNode(planViewVOS, planViewVO);
                }
                break;
            } else {
                if (planViewVO.getChildCount() != 0) {
                    setProgressToParent(currentPresent, currentNode, planViewVO.getChildren());
                }
            }
        }

    }

    /**
     * 查找当前节点的上级
     * @param planViewVOS 所有计划节点
     * @param childNodeVo 子节点
     */
    private void findParentNode(ProjectPlanViewVO[] planViewVOS, ProjectPlanViewVO childNodeVo) {
        for (ProjectPlanViewVO planViewVO : planViewVOS) {
            if (childNodeVo.getPkParent().equalsIgnoreCase(planViewVO.getPkPlan())) {
                this.computedParentProgress(planViewVO.getChildren(), childNodeVo,planViewVOS);
            } else {
                if (planViewVO.getChildCount() != 0) {
                    this.findParentNode(planViewVO.getChildren(), childNodeVo);
                }
            }
        }
    }

    /**
     * 将计划按项目进行组装
     * @param planViewVOS 计划VO
     * @param filterCode ongoing（进行中）、completed（已完成）、all（全部）
     * @return 项目VO
     */
    private List<ProjectVO> coverToProjectVo(ProjectPlanViewVO[] planViewVOS, String filterCode, String orgId) {
        Map<String, List<ProjectPlanViewVO>> projectIdMap = new HashMap<>();
        for (ProjectPlanViewVO planViewVO : planViewVOS) {
            // 将项目按组织进行过滤
            if(StringUtils.isNotEmpty(orgId) && !orgId.equalsIgnoreCase(planViewVO.getPkOrg())){
                continue;
            }

            if (projectIdMap.get(planViewVO.getPkProject()) != null) {
                projectIdMap.get(planViewVO.getPkProject()).add(planViewVO);
            } else {
                List<ProjectPlanViewVO> planViewVOList = new ArrayList<>();
                planViewVOList.add(planViewVO);
                projectIdMap.put(planViewVO.getPkProject(), planViewVOList);
            }
        }
        List<ProjectVO> projectVOList = new ArrayList<>(projectIdMap.size());
        for (Map.Entry<String, List<ProjectPlanViewVO>> entry : projectIdMap.entrySet()) {
            ProjectVO projectVO = new ProjectVO();
            projectVO.setPkProject(entry.getKey());
            int totalDay = 0;
            int totalProgress = 0;
            Date minStartTime = null;
            Date maxEndTime = null;
            for (ProjectPlanViewVO planViewVO : entry.getValue()) {
                // 项目的计划开始时间和结束时间分别取计划当中的第一个根节点的开始时间和最后一个根节点的结束时间
                if (minStartTime == null || minStartTime.after(planViewVO.getExpeStartDate())) {
                    minStartTime = planViewVO.getExpeStartDate();
                }
                if (maxEndTime == null || maxEndTime.before(planViewVO.getExpeEndDate())) {
                    maxEndTime = planViewVO.getExpeEndDate();
                }
                // 通过根节点进度计算项目的总进度
                totalDay += planViewVO.getExpeDuration();
                totalProgress += planViewVO.getProgress() * planViewVO.getExpeDuration();
                projectVO.setScode(planViewVO.getProjectCode());
                projectVO.setSname(planViewVO.getProjectName());
                projectVO.setManager(planViewVO.getProjectManager());
                projectVO.setIskey(planViewVO.getIsKey());
                // 计算按时完成率，按时完成的叶子节点个数／完成的叶子节点个数
                this.computedOnTimeRate(planViewVO);
            }
            projectVO.setExpeStartDate(minStartTime);
            projectVO.setExpeEndDate(maxEndTime);
            int currentProgress = (int) Math.floor(totalProgress / totalDay);
            projectVO.setProgress(currentProgress);
            if (totalFinishCount != 0) {
                projectVO.setOnTimeCompletionRate((int) Math.floor((onTimeCount * 100 / totalFinishCount)));
                totalFinishCount = 0;
                onTimeCount = 0;
            }
            if(FILTER_CODE_ONGOING.equalsIgnoreCase(filterCode) && currentProgress != 100) {
                projectVOList.add(projectVO);
            }else if(FILTER_CODE_COMPLETED.equalsIgnoreCase(filterCode) && currentProgress == 100) {
                projectVOList.add(projectVO);
            }else if(FILTER_CODE_ALL.equalsIgnoreCase(filterCode)){
                projectVOList.add(projectVO);
            }
        }
        return projectVOList;
    }

    /**
     * 计算按时完成率
     * @param planViewVO 计划VO
     */
    private void computedOnTimeRate(ProjectPlanViewVO planViewVO) {
        if (planViewVO.getChildCount() == 0 && !planViewVO.getPkPlan().equalsIgnoreCase(planViewVO.getPkRoot()) &&
                planViewVO.getProgress() == 100) {
            totalFinishCount++;
            if (planViewVO.getEndDate() != null && planViewVO.getEndDate().before(planViewVO.getExpeEndDate())) {
                onTimeCount++;
            }
        } else if (planViewVO.getChildCount() != 0) {
            if (ArrayUtils.isEmpty(planViewVO.getChildren()))
                return;

            for (ProjectPlanViewVO vo : planViewVO.getChildren()) {
                this.computedOnTimeRate(vo);
            }
        }
    }

    private void setDependPlanName(ProjectPlanViewVO[] allVOs){
        Map<String, ProjectPlanViewVO> planViewVOMap = new HashMap<String, ProjectPlanViewVO>();
        for(ProjectPlanViewVO vo : allVOs){
            planViewVOMap.put(vo.getPkPlan(), vo);
        }
        if(planViewVOMap != null){
            for(ProjectPlanViewVO vo : allVOs){
                if(StringUtils.isNotBlank(vo.getDependPlan())){
                    ProjectPlanViewVO viewVO = planViewVOMap.get(vo.getDependPlan());
                    if(viewVO != null){
                        vo.setDependPlanName(viewVO.getScode());
                    }
                }
            }
        }
    }

    public Map<String, List<ProjectPlanViewVO>> genChildMap(ProjectPlanViewVO[] allVOs) {
        // 构造树
        Map<String, List<ProjectPlanViewVO>> childMap = new HashMap<>();
        for (ProjectPlanViewVO vo : allVOs) {
            vo.setKey(vo.getPkPlan());
            String parent = vo.getPkParent();
            if (StringUtils.isEmpty(parent)) {
                parent = StringUtils.EMPTY;
            }
            List<ProjectPlanViewVO> children = childMap.get(parent);
            if (children == null) {
                children = new ArrayList<>();
                childMap.put(parent, children);
            }
            children.add(vo);
        }
        for (ProjectPlanViewVO vo : allVOs) {
            vo.setScode(null);
        }
        for (ProjectPlanViewVO vo : allVOs) {
            List<ProjectPlanViewVO> children = childMap.get(vo.getPkPlan());
            if (children != null) {
                vo.setChildren(children.toArray(new ProjectPlanViewVO[0]));
            }
        }
        return childMap;
    }

    private void genCodeOfTree(ProjectPlanViewVO parent, List<ProjectPlanViewVO> tree, Map<String, List<ProjectPlanViewVO>> childMap) {
        for (ProjectPlanViewVO plan : tree) {
            if (parent == null) {
                plan.setScode("0");
            } else if(StringUtils.isBlank(parent.getScode()) || parent.getScode().equals("0")){
                plan.setScode(Integer.toString(plan.getSeq()));
            } else{
                plan.setScode(parent.getScode() + '.' + plan.getSeq());
            }
            List<ProjectPlanViewVO> children = childMap.get(plan.getPkPlan());
            if (children != null && !children.isEmpty()) {
                // 递归应该不会出问题，最深20层
                genCodeOfTree(plan, children, childMap);
            }
            if(parent != null){
                if(parent.getStartDate() == null || (plan.getStartDate() != null && plan.getStartDate().compareTo(parent.getStartDate()) < 0)){
                    parent.setStartDate(plan.getStartDate());
                }
                if (PlanProgressStatusConst.PLAN_COMPLETED.equals(parent.getCompletedStatus())
                        && PlanProgressStatusConst.PLAN_COMPLETED.equals(plan.getCompletedStatus())
                        && (parent.getEndDate() == null || (plan.getEndDate() != null && plan.getEndDate().compareTo(parent.getEndDate()) > 0))) {
                    parent.setEndDate(plan.getEndDate());
                }
            }
        }
    }

    @PostMapping("/savePlan")
    public void savePlan(@RequestBody ProjectPlanVO plan) throws BusinessException {
        if(plan == null) throw new BusinessException("根计划不能为空！");
        if(StringUtils.isBlank(plan.getPkProject())) throw new BusinessException("项目不能为空！");
        if(StringUtils.isBlank(plan.getSname())) throw new BusinessException("根计划名称不能为空");
        maintain.savePlan(plan);
    }

    @RequestMapping("/commit")
    @PMApprove(trigger = "commit")
    public ProjectPlanVO commit(@RequestBody ProjectPlanVO plan) throws BusinessException {
        log.debug("进入计划编制提交controller");
        if(StringUtils.isBlank(plan.getPkPlan())){
            return null;
        }
        plan.setPkTenant(RuntimeEnvironment.getTenantId());
        return maintain.commit(plan);
    }

    @PostMapping("/adjustPlan")
    public void adjustPlan(@RequestBody ProjectPlanVO plan) throws BusinessException {
        if(plan == null) throw new BusinessException("根计划不能为空！");
        if(StringUtils.isBlank(plan.getPkProject())) throw new BusinessException("项目不能为空！");
        if(StringUtils.isBlank(plan.getSname())) throw new BusinessException("根计划名称不能为空");
        maintain.updatePlanforAdjust(plan);
    }

    @GetMapping("/queryByRoot")
    public ProjectPlanViewVO[] queryByRoot(@RequestParam("rootId") String rootId) throws BusinessException {
        ProjectPlanViewVO[] allVOs = query.queryViewByRoot(rootId);
        if (ArrayUtils.isEmpty(allVOs)) {
            return allVOs;
        }
        setDependPlanName(allVOs);

        transAndFillViewVO(allVOs);

        String[] userFields = new String[]{ProjectPlanViewVO.MANAGER, ProjectPlanViewVO.PLAN_FINISHER};
        String[] userNameFields = new String[]{ProjectPlanViewVO.MANAGER_NAME, ProjectPlanViewVO.PLAN_FINISHER_NAME};
        userInfoQuery.tranUserInfoField(allVOs, userFields, userNameFields);
        Map<String, List<ProjectPlanViewVO>> childMap = genChildMap(allVOs);
        List<ProjectPlanViewVO> result = childMap.get(StringUtils.EMPTY);
        if (result == null) {
            return new ProjectPlanViewVO[0];
        } else {
            genCodeOfTree(null, result, childMap);
            return result.toArray(new ProjectPlanViewVO[0]);
        }
    }

    @PostMapping("/delete")
    public int deletePlanRecursive(@RequestBody ProjectPlanVO plan, BindingResult errors) throws BusinessException {
        if (StringUtils.isEmpty(plan.getPkPlan())) {
            errors.rejectValue(ProjectPlanVO.PK_PLAN, null);
        }
        if (StringUtils.isEmpty(plan.getChangeFlag())) {
            errors.rejectValue(CommonFieldConst.CHANGE_FLAG, null);
        }
        if (errors.hasErrors()) {
            throw new IllegalRequestArgException();
        }
        maintain.deletePlan(plan.getPkPlan(), plan.getChangeFlag());
        return 1;
    }

    @GetMapping("/search")
    @ResponseBody
    public UserInfo[] searchUser(@RequestParam(value = "keyword", required = false) String keyword,
                                 @RequestParam("projectId") String projectId) throws BusinessException {
        return searchUsers(keyword, projectId, 1, 1000);
    }

    private UserInfo[] searchUsers(String keyword, String projectId, int pageNum, int pageSize) throws BusinessException {
        final TeamMemberVO[] teamMembers = teamMemberQuery.queryByProject(projectId);
        Map<String, String> userMap = new HashMap<String, String>();
        for (int i = 0; i < teamMembers.length; i++) {
            userMap.put(teamMembers[i].getPkUser(), teamMembers[i].getPkTeamMember());
        }
        UserSearchResultVO.ContentVO result = userInfoQuery.searchUsers(RuntimeEnvironment.getTenantId(), keyword, pageNum, pageSize);

        List<UserInfo> userInfoList = new ArrayList<UserInfo>();

        if(result == null || result.getContent() == null){
            return new UserInfo[0];
        } else{
            for(UserInfo user : result.getContent()){
                if(userMap.get(user.getUserId()) != null){
                    userInfoList.add(user);
                }
            }
        }
        return userInfoList.isEmpty() ? new UserInfo[0] : userInfoList.toArray(new UserInfo[0]);
    }

    @RequestMapping("/importPlanFromExcel")
    @ResponseBody
    public ProjectPlanVO importPlanFromExcel(@RequestParam(value="file") MultipartFile file,
                                             ProjectPlanVO planRoot) throws BusinessException {

        if(planRoot == null) throw new BusinessException("根计划不能为空！");
        if(planRoot.getPkProject() == null) throw new BusinessException("项目不能为空！");
        if(file == null) throw new BusinessException("导入文件为空！");
        String fileOriName = file.getOriginalFilename();
        if(!StringUtils.isEmpty(fileOriName)){
            if(!fileOriName.endsWith(".xls") && !fileOriName.endsWith(".xlsx")){
                throw new BusinessException("导入文件不是Excel格式！");
            }
        }else{
            throw new BusinessException("文件信息不全！");
        }

        List<ProjectPlanImportVO> planList = analyzeExcel(file); //解析Excel
        if(planList == null || planList.size() == 0) return planRoot;

        planRoot.setScode("0");  //为了方便处理，根节点编码设为0
        ProjectPlanVO[] planVOS = new ProjectPlanVO[planList.size() + 1];
        planVOS[0] = planRoot;

        int index = 0;
        for(ProjectPlanImportVO importVO : planList){
            planVOS[++index] = new ProjectPlanVO();
            planVOS[index].setPkProject(planRoot.getPkProject());
            if(importVO == null) continue;
            planVOS[index].setSname(importVO.getSname());
            planVOS[index].setScode(importVO.getScode());
            planVOS[index].setExpeStartDate(importVO.getExpeStartDate());
            planVOS[index].setExpeEndDate(importVO.getExpeEndDate());
            planVOS[index].setExpeDuration(importVO.getExpeDuration());
            planVOS[index].setManagerName(importVO.getManagerName());
            planVOS[index].setDependPlanName(importVO.getDependPlanName());
            planVOS[index].setMilestoneCode(importVO.getMilestoneCode());
        }
        //setPkByManagerName(planVOS, planRoot.getPkProject());  //根据负责人名字，设置负责人主键
        planInsertFromExcelCheck(planVOS);  //校验excel导入数据
        // 翻译里程碑编码
        translateMilestoneCode(planVOS);
        buildPlanTree(planVOS, planRoot); //构建树并设置主键等信息
        resetDate(planRoot);    //更新计划开始、完成日期，工期
        return planRoot;
    }

    /**
     * 翻译里程碑编码，得到主键和名字
     *
     * @param planVOS
     */
    private void translateMilestoneCode(ProjectPlanVO[] planVOS) throws BusinessException {
        Set<String> milestoneCodeSet = new HashSet<>();
        for (ProjectPlanVO vo : planVOS) {
            if (StringUtils.isEmpty(vo.getMilestoneCode()))
                continue;

            milestoneCodeSet.add(vo.getMilestoneCode());
        }

        if (milestoneCodeSet.size() == 0)
            return;

        // 查询里程碑档案
        Map<String, MilestoneVO> milestoneVOMap = milestoneQuery.queryByCodes(milestoneCodeSet.toArray(new String[0]),
                RuntimeEnvironment.getTenantId());

        for (ProjectPlanVO planVO : planVOS) {
            if (StringUtils.isEmpty(planVO.getMilestoneCode()))
                continue;

            MilestoneVO milestoneVO = milestoneVOMap.get(planVO.getMilestoneCode());
            if (milestoneVO == null)
                continue;

            planVO.setPkMilestone(milestoneVO.getPkMilestone());
            planVO.setMilestoneName(milestoneVO.getName());
        }
    }

    /**
     * 解析Excel
     */
    private List<ProjectPlanImportVO> analyzeExcel(MultipartFile file) throws BusinessException {
        //导入Excel
        ImportParams params = new ImportParams();
        params.setTitleRows(0);
        params.setHeadRows(1);
        params.setNeedVerfiy(true);
        ExcelImportResult<ProjectPlanImportVO> result;
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
            result = ExcelImportUtil.importExcelMore(inputStream, ProjectPlanImportVO.class, params);
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        } finally {
            if(inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    //ignore
                }
            }
        }
        if(result == null) return new ArrayList<>();
        if(!result.getFailList().isEmpty()){
            throw new BusinessException("请按限定格式导入。");
        }
        return result.getList();
    }
    /**
     * 根据负责人名字，设置负责人主键
     */
    private void setPkByManagerName(ProjectPlanVO[] planVOS, String pkProject) throws BusinessException {
        TeamMemberVO[] teamMembers = teamMemberQuery.queryByProject(pkProject);
        if(teamMembers == null) return;
        String[] pkTeamMembers = new String[teamMembers.length];
        for(int i = 0; i < teamMembers.length; i++){
            if(teamMembers[i] != null){
                pkTeamMembers[i] = teamMembers[i].getPkUser();
            }
        }
        UserInfo[] userInfos = userInfoQuery.queryUsers(pkTeamMembers);
        if(userInfos == null) return;
        HashMap<String, String> recordPk = new HashMap<>();
        for(UserInfo userInfo : userInfos){
            if(recordPk.get(userInfo.getUserName()) == null){      //如果名字出现重复，取第一个
                recordPk.put(userInfo.getUserName(), userInfo.getUserId());
            }
        }
        for(ProjectPlanVO planVO : planVOS){
            if(recordPk.get(planVO.getManagerName()) != null){
                planVO.setManager(recordPk.get(planVO.getManagerName()));
            }else {
                planVO.setManagerName(null);
            }
        }
    }
    /**
     * 校验Excel导入的计划节点，计划名称，计划开始、完成日期是否为空，层次是否超过20层
     */
    private void planInsertFromExcelCheck(ProjectPlanVO[] planVOS) throws BusinessException{
        if(planVOS == null || planVOS.length < 1) return;
        for(int i = 1; i < planVOS.length; i++){
            if(planVOS[i] != null){
                if(StringUtils.isEmpty(planVOS[i].getSname())){
                    throw new BusinessException("计划名称不能为空！");
                }
                if(planVOS[i].getExpeStartDate() == null || planVOS[i].getExpeEndDate() == null){
                    throw new BusinessException("计划开始日期和计划完成日期不能为空！");
                }
                if(planVOS[i].getExpeStartDate().after(planVOS[i].getExpeEndDate())){    //严格晚于
                    throw new BusinessException("计划开始日期不能晚于计划完成日期！");
                }
                String code = planVOS[i].getScode();
                int level = 1, MAX_LEVEL = 20;
                for (int j = 0; j < code.length(); j++) {
                    if (code.charAt(j) == '.') {
                        level++;
                        if (level > MAX_LEVEL) {
                            throw new BusinessException("计划超过最大层级限制！");
                        }
                    }
                }
            }
        }
    }
    /**
     * 通过子节点更新父节点的计划开始、完成日期，以及工期
     */
    private void resetDate(ProjectPlanVO root){
        if(root == null || root.getChildren() == null){
            if(root != null && root.getExpeStartDate() != null && root.getExpeEndDate() != null){
                root.setExpeDuration((int) DateUtils.getDaysBetween(root.getExpeStartDate(), root.getExpeEndDate()));
            }
            return;
        }
        Date minStartDate = null, maxEndDate = null;
        for(ProjectPlanVO child : root.getChildren()){
            if(child == null) continue;
            resetDate(child);
            if(minStartDate == null || child.getExpeStartDate().before(minStartDate)){
                minStartDate = child.getExpeStartDate();
            }
            if(maxEndDate == null || child.getExpeEndDate().after(maxEndDate)){
                maxEndDate = child.getExpeEndDate();
            }
        }
        if(minStartDate != null && maxEndDate != null){
            root.setExpeStartDate(minStartDate);
            root.setExpeEndDate(maxEndDate);
            root.setExpeDuration((int)DateUtils.getDaysBetween(minStartDate, maxEndDate));
        }
    }
    /**
     * 根据计划编码构造树形关系，并设置前置计划等信息, 输入数据必须要保证父亲节点在前。
     */
    private void buildPlanTree(ProjectPlanVO[] planVOS, ProjectPlanVO root) {

        HashMap<String, String> recordOldCode = new HashMap<>();  //根据旧编码找新编码

        HashMap<String, ArrayList<ProjectPlanVO>> recordChild = new HashMap<>(); //记录所有节点的孩子
        recordChild.put(root.getScode(), new ArrayList<ProjectPlanVO>()); //放入根节点
        if(root.getChildCount() == null) root.setChildCount(0);

        for(int i = 1; i < planVOS.length; i++){ //跳过根节点
            ProjectPlanVO planVO = planVOS[i];
            if(planVO == null || planVO.getScode() == null) continue;
            String[] codes = planVO.getScode().split("\\.");

            if(codes.length == 1){  //一级节点
                recordChild.get(root.getScode()).add(planVO);
                //重新设置当前节点的编码
                String newCode = (recordChild.get(root.getScode()).size() + root.getChildCount()) + "";
                recordOldCode.put(planVO.getScode(), newCode);
                planVO.setScode(newCode);
                //设置兄弟间序号
                planVO.setSeq(recordChild.get(root.getScode()).size() + root.getChildCount());

            }else if(codes.length  > 1){
                int endIndex = planVO.getScode().length() - codes[codes.length - 1].length() - 1;
                //截取出最长前缀作为父节点编码
                String oldParentCode = planVO.getScode().substring(0, endIndex);
                //根据父节点旧编码查找新编码
                String newParentCode = recordOldCode.get(oldParentCode);
                if(StringUtils.isEmpty(newParentCode)) continue;
                //父节点的孩子加入当前VO
                if(recordChild.get(newParentCode) == null){
                    recordChild.put(newParentCode, new ArrayList<ProjectPlanVO>());
                }
                recordChild.get(newParentCode).add(planVO);
                //重新设置当前节点的编码
                String newCode = newParentCode + "." + recordChild.get(newParentCode).size();
                recordOldCode.put(planVO.getScode(), newCode);
                planVO.setScode(newCode);
                //设置兄弟间序号
                planVO.setSeq(recordChild.get(newParentCode).size());
            }
        }
        for(ProjectPlanVO planVO : planVOS){
            if(planVO.getDependPlanName() != null){  //前置计划编码修改为新的编码
                planVO.setDependPlanName(recordOldCode.get(planVO.getDependPlanName()));
            }
            if(recordChild.get(planVO.getScode()) != null){
                planVO.setChildCount(recordChild.get(planVO.getScode()).size());
                planVO.setChildren(recordChild.get(planVO.getScode()).toArray(new ProjectPlanVO[0]));
            }
        }
    }

    @PostMapping(value = "/importProject")
    @ResponseBody
    public ProjectPlanVO importProject(ProjectPlanVO planRoot, @RequestParam(value="file") MultipartFile file) throws BusinessException {
        if(planRoot == null) throw new BusinessException("根计划不能为空！");
        if(StringUtils.isBlank(planRoot.getPkProject())) throw new BusinessException("项目不能为空！");
        if(file == null) throw new BusinessException("导入文件为空！");
        //校验文件后缀
        String fileName = file.getOriginalFilename();
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        if(!suffix.equals("mpp")) throw new BusinessException("文件格式错误");

        InputStream in = null;
        try{
            in = file.getInputStream();
            MPPReader mppRead = new MPPReader();
            ProjectFile pf = mppRead.read(in);
            List<Task> tasks = pf.getChildTasks();
            Map<String, String> planMap = new HashMap<>();
            getChildrenTask(tasks.get(0), planRoot, planMap);
            setDependPlanName(planRoot, planMap);
        } catch (MPXJException e) {
            throw new BusinessException("文件解析失败");
        } catch (Exception e) {
            throw new BusinessException("导入失败");
        } finally {
            if(in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return planRoot;
    }

    public void getChildrenTask(Task task, ProjectPlanVO planVO, Map<String, String> planMap) throws BusinessException {
        if(task.getResourceAssignments().size() == 0){
            List<Task> tasks = task.getChildTasks();
            for (int i = 0; i < tasks.size(); i++) {
                if(tasks.get(i).getResourceAssignments().size() == 0){//说明还是在父任务层
                    //格式化开始完成日期
                    Date startDate = formatDate(tasks.get(i).getStart());
                    Date endDate = formatDate(tasks.get(i).getFinish());
                    ProjectPlanVO pro = new ProjectPlanVO();
                    //构建子计划
                    buildPlan(planVO, pro, tasks.get(i), startDate, endDate, planMap);
                    getChildrenTask(tasks.get(i), pro, planMap);
                }else{
                    getChildrenTask(tasks.get(i), planVO, planMap);
                }
            }
        }else{
            getResourceAssignment(task, planVO, planMap);
        }
    }

    public void getResourceAssignment(Task task, ProjectPlanVO planVO, Map<String, String> planMap) throws BusinessException {
        List<ResourceAssignment> list = task.getResourceAssignments();
        if(!list.isEmpty()){
            ResourceAssignment rs = list.get(0);
            //末级开始完成时间从ResourceAssignment中读取
            Date startDate = formatDate(rs.getStart());
            Date endDate = formatDate(rs.getFinish());
            ProjectPlanVO pro = new ProjectPlanVO();
            pro.setChildCount(0);
            //构建计划
            buildPlan(planVO, pro, task, startDate, endDate, planMap);
            //校验末级节点层级
            planLevelCheck(pro);
        }
    }

    private void planLevelCheck(ProjectPlanVO plan) throws BusinessException {
        String code = plan.getScode();
        int level = 1, maxLevel = 20;
        for (int i = 0; i < code.length(); i++) {
            if (code.charAt(i) == '.') {
                level++;
            }
        }
        if (level > maxLevel) {
            throw new BusinessException("计划超过最大层级限制！");
        }
    }

    private void buildPlan(ProjectPlanVO parent, ProjectPlanVO child, Task task, Date startDate, Date endDate, Map<String, String> planMap) throws BusinessException {
        if(StringUtils.isBlank(task.getName())) throw new BusinessException("计划名称不能为空！");
        if(startDate == null || endDate == null) throw new BusinessException("计划开始完成日期不能为空！");
        //设置上级计划childCount
        int childCount = parent.getChildCount() == null ? 0 : parent.getChildCount();
        parent.setChildCount(childCount + 1);
        //构建子计划
        child.setPkProject(parent.getPkProject());
        child.setSname(task.getName());
        if(StringUtils.isBlank(parent.getScode()) || parent.getScode().equals("0")){
            child.setScode(parent.getChildCount().toString());
        }else{
            child.setScode(parent.getScode()+"."+parent.getChildCount());
        }
        child.setSeq(parent.getChildCount());
        child.setExpeStartDate(startDate);
        child.setExpeEndDate(endDate);
        //计算工期
        int days = (int) DateUtils.getDaysBetween(startDate, endDate);
        child.setExpeDuration(days);

        if(!task.getPredecessors().isEmpty()){
            Relation relation = task.getPredecessors().get(0);
            Task targetTask = relation.getTargetTask();
            if(targetTask != null){
                child.setDependPlanName(targetTask.getID().toString());
            }
        }
        planMap.put(task.getID().toString(), child.getScode());

        ProjectPlanVO[] childs = parent.getChildren();
        if(childs == null) childs = new ProjectPlanVO[0];
        parent.setChildren(ArrayUtils.add(childs, child));

        if(StringUtils.isBlank(parent.getScode()) || parent.getScode().equals("0")){
            if(parent.getExpeStartDate() == null || (child.getExpeStartDate() != null && child.getExpeStartDate().compareTo(parent.getExpeStartDate()) < 0)){
                parent.setExpeStartDate(child.getExpeStartDate());
            }
            if(parent.getExpeEndDate() == null || (child.getExpeEndDate() != null && child.getExpeEndDate().compareTo(parent.getExpeEndDate()) > 0)){
                parent.setExpeEndDate(child.getExpeEndDate());
            }
            parent.setExpeDuration((int)DateUtils.getDaysBetween(parent.getExpeStartDate(), parent.getExpeEndDate()));
        }
    }

    private void setDependPlanName(ProjectPlanVO planVO, Map<String, String> planMap){
        if(StringUtils.isBlank(planVO.getPkPlan()) && StringUtils.isNotBlank(planVO.getDependPlanName())){
            planVO.setDependPlanName(planMap.get(planVO.getDependPlanName()));
        }
        if(planVO.getChildren() != null){
            for(ProjectPlanVO vo : planVO.getChildren()){
                setDependPlanName(vo, planMap);
            }
        }
    }

    private Date formatDate(Date date) {
        if (date != null) {
            try {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                String dateStr = sdf.format(date);
                return sdf.parse(dateStr);
            } catch(ParseException e){
                e.printStackTrace();
            }
        }
        return null;
    }

    @PostMapping(value = "/importTemplet")
    @ResponseBody
    public ProjectPlanVO importTemplet(@RequestBody ProjectPlanVO planVO) throws BusinessException {

        if (planVO == null) throw new BusinessException("根计划不能为空！");
        if (StringUtils.isBlank(planVO.getPkProject())) throw new BusinessException("项目不能为空！");
        if (planVO.getTempletVO() == null) throw new BusinessException("模板不能为空！");
        Map<String, String> planMap = new HashMap<>();
        buildPlan(planVO.getTempletVO(), planVO, planMap);
        setDependPlanName(planVO, planMap);
        return planVO;
    }

    private void buildPlan(PlanTempletVO tempVO, ProjectPlanVO planVO, Map<String, String> planMap) throws BusinessException {
        if(tempVO.getChildren() != null){
            for(PlanTempletVO vo : tempVO.getChildren()){
                ProjectPlanVO pro = new ProjectPlanVO();
                convert(planVO, pro, vo, planMap);
                buildPlan(vo, pro, planMap);
            }
        }
    }

    private void convert(ProjectPlanVO parent, ProjectPlanVO child, PlanTempletVO tempVO, Map<String, String> planMap) throws BusinessException {
        //设置上级计划childCount
        int childCount = parent.getChildCount() == null ? 0 : parent.getChildCount();
        parent.setChildCount(childCount + 1);
        //构建子计划
        child.setPkProject(parent.getPkProject());
        child.setSname(tempVO.getSname());
        if(StringUtils.isBlank(parent.getScode()) || parent.getScode().equals("0")){
            child.setScode(parent.getChildCount().toString());
        }else{
            child.setScode(parent.getScode()+"."+parent.getChildCount());
        }
        child.setSeq(parent.getChildCount());
        if (tempVO.getExpectDuration() != null) {
            child.setExpeDuration(tempVO.getExpectDuration());
            child.setExpeStartDate(formatDate(new Date()));
            child.setExpeEndDate(formatDate(org.apache.commons.lang3.time.DateUtils.addDays(new Date(), tempVO.getExpectDuration() - 1)));
        } else {
            child.setExpeDuration(1);
            child.setExpeStartDate(formatDate(new Date()));
            child.setExpeEndDate(formatDate(new Date()));
        }

        child.setRelatedBillType(tempVO.getRelatedBillType());
        child.setManager(tempVO.getManager());
        child.setManagerName(tempVO.getManagerName());
        child.setPkMilestone(tempVO.getPkMilestone());
        child.setMilestoneName(tempVO.getMilestoneName());
        child.setRelatedBillType(tempVO.getRelatedBillType());
        child.setRelatedBillTypeName(tempVO.getRelatedBillTypeName());
        child.setPkDepartment(tempVO.getPkDepartment());
        child.setDepartmentName(tempVO.getDepartmentName());

        planMap.put(tempVO.getPkPlanTemplet(), child.getScode());
        child.setDependPlanName(tempVO.getDependTemplet());

        ProjectPlanVO[] childs = parent.getChildren();
        if(childs == null) childs = new ProjectPlanVO[0];
        parent.setChildren(ArrayUtils.add(childs, child));

    }
}
